import jwt

from datetime import datetime, timedelta, timezone
from typing import Optional, Dict

from fastapi import HTTPException, status, Depends
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session

from app.db.db_handler import get_database_handler, get_db
from app.db.db_models import User
from app.db.db_auth import DBAuth

# 秘钥和算法
SECRET_KEY = "your_secret_key"  # 强烈建议使用环境变量存储
ALGORITHM = "HS256"

ACCESS_TOKEN_EXPIRE_MINUTES = 30  # 访问令牌过期时间
REFRESH_TOKEN_EXPIRE_DAYS = 7     # 刷新令牌过期时间

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None, exp: Optional[datetime] = None) -> str:
    """
    创建 JWT token
    :param data: 要编码的数据，通常包含用户标识 
    :param expires_delta: 过期时间增量，None 则使用默认值
    :return: 编码后的 JWT token
    """
    to_encode = data.copy()
    if exp:
        expire = exp
    else:
        if expires_delta:
            expire = datetime.now(timezone.utc) + expires_delta
        else:
            expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

def verify_token(token: str) -> dict:
    """验证 JWT token"""
    try:
        # 1. 解码并验证签名
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        
        # 2. 检查必要字段
        username: str = payload.get("sub")
        if not username:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Could not validate credentials",
                headers={"WWW-Authenticate": "Bearer"},
            )
            
        # 3. 检查过期时间（JWT自动处理）
        # 如果令牌过期，会抛出 ExpiredSignatureError
        
        return payload
        
    except jwt.ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token has expired",
            headers={"WWW-Authenticate": "Bearer"},
        )
    except jwt.JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Could not validate credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )


def create_refresh_token(data: dict) -> str:
    """
    创建刷新令牌
    :param data: 要编码的数据，通常包含用户标识
    :return: 刷新令牌
    """
    to_encode = data.copy()
    # 刷新令牌有效期更长（例如7天）
    expire = datetime.now(timezone.utc) + timedelta(days=7)
    to_encode.update({"exp": expire, "type": "refresh"})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def refresh_access_token(refresh_token: str) -> dict:
    """
    使用刷新令牌获取新的访问令牌
    :param refresh_token: 刷新令牌
    :return: 新的访问令牌和类型
    """
    try:
        # 1. 验证刷新令牌
        payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
        
        # 2. 确认是刷新令牌
        if payload.get("type") != "refresh":
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid refresh token",
            )
        
        # 3. 生成新的访问令牌
        access_token = create_access_token({"sub": payload["sub"]})
        return {
            "access_token": access_token,
            "token_type": "bearer"
        }
        
    except jwt.ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Refresh token has expired",
        )
    except jwt.JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Could not validate refresh token",
        ) 
    

def get_current_user(db: Session=Depends(get_db), token: str = Depends(oauth2_scheme)) -> User:
    payload = verify_token(token)
    username: str = payload.get("sub")
    if username is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌载荷无效",
            headers={"WWW-Authenticate": "Bearer"},
        )

    db_auth = DBAuth(db)
    user = db_auth.get_user(username)
    
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户未找到",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    return user

def get_current_active_user(current_user: User = Depends(get_current_user)) -> User:
    if current_user.disabled:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="用户已禁用",
        )
    return current_user